<?php

/**
 * @desc		框架扩展：数据库操作类库|mongodb
 * ---------------------------------------------------------------------
 * @author	unphp <unphp@qq.com>
 * @date		2014-03-27
 * @copyright	UnPHP 1.1
 * ---------------------------------------------------------------------
 */

namespace UnPHP\Lib\DBdriver;

use DBInterface;
use MongoClient;
use MongoCollection;
use MongoInt64;

class MongoEngine implements DBInterface
{

    /**
     *
     * @var PoolClient 
     */
    private $_poolClient = null;
    private $_conn = null;
    private $_last_error_info;

    public function __construct(PoolClient $poolClient)
    {
        $this->_poolClient = $poolClient;
    }

    private function _getPoolClient()
    {
        return $this->_poolClient;
    }

    protected function _getConn()
    {
        if (null === $this->_conn)
        {
            $dsn = 'mongodb://';
            $dsn .= $this->_getPoolClient()->host . ':' . $this->_getPoolClient()->port;
            $potions = array();
            $potions['connect'] = true;
            $potions['db'] = $this->_getPoolClient()->dbname;
            $client = new MongoClient($dsn, $potions);
            $this->_conn = $client->selectDB($this->_getPoolClient()->dbname);
        }
        return $this->_conn;
    }

    /**
     * 获取表集合名称
     * @return MongoCollection
     */
    protected function _getCollection($collectionName)
    {
        $conn = $this->_getConn();
        $tableName = $this->_getPoolClient()->prefix . $collectionName;
        return $conn->selectCollection($tableName);
    }

    public function getPk($collectionName)
    {
        
    }

    public function errorInfo()
    {
        return $this->_last_error_info;
    }

    public function begin()
    {
        //return $this->_getConn()->beginTransaction();
    }

    public function commit()
    {
        //return $this->_getConn()->commit();
    }

    public function rollback()
    {
        //return $this->_getConn()->rollBack();
    }

    /**
     * 
     * @param type $collectionName
     * @param type $condition
     * @param type $options
     * @return type
     */
    public function findOne($collectionName, $condition = array(), $options = array())
    {
        $collection = $this->_getCollection($collectionName);
        $parseCondition = array();
        $this->_queryCondition($condition, $parseCondition);
        $rs = $collection->findOne($parseCondition, $options);
        if ($rs)
        {
            $res = object_to_array($rs);
            unset($res['_id']);
            return $res;
        }
        return null;
    }

    /**
     * 
     * @param type $collectionName
     * @param type $condition
     * @param type $options
     * @return type
     */
    public function findAll($collectionName, $condition = array(), $options = array())
    {
        $ret = array();
        $collection = $this->_getCollection($collectionName);
        $parseCondition = array();
        $this->_queryCondition($condition, $parseCondition);
        if (isset($options['fields']))
        {
            $documents = $collection->find($parseCondition, $options['fields']);
        }
        else
        {
            $documents = $collection->find($parseCondition);
        }
        if (isset($options['sort']))
        {
            $documents->sort($options['sort']);
        }
        if (isset($options['offset']))
        {
            $documents->skip($options['offset']);
        }
        if (isset($options['limit']))
        {
            $documents->limit($options['limit']);
        }
        while ($documents->hasNext())
        {
            $document = $documents->getNext();
            $temp = object_to_array($document);
            unset($temp['_id']);
            $ret[] = $temp;
        }
        return $ret;
    }

    /**
     * 
     * @param type $collectionName
     * @param type $condition
     * @return type
     */
    public function count($collectionName, $condition = array())
    {
        $collection = $this->_getCollection($collectionName);
        $parseCondition = array();
        $this->_queryCondition($condition, $parseCondition);
        $documents = $collection->count($parseCondition);
        return $documents;
    }

    public function insert($collectionName, $new, $options = array())
    {
        $collection = $this->_getCollection($collectionName);
        $this->_write_value($new);
        return $collection->insert($new, $options);
    }

    public function batchInsert($collectionName, $new, $options = array())
    {
        $collection = $this->_getCollection($collectionName);
        $parseNew = array();
        foreach ($new as $row)
        {
            $this->_write_value($row);
            $parseNew[] = $row;
        }
        unset($new);
        return $collection->batchInsert($parseNew, $options);
    }

    public function remove($collectionName, $condition = array(), $options = array())
    {
        $collection = $this->_getCollection($collectionName);
        $parseCondition = array();
        $this->_queryCondition($condition, $parseCondition);
        return $collection->remove($parseCondition, $options);
    }

    public function update($collectionName, $condition, $new, $options = array())
    {
        if ($new)
        {
            $collection = $this->_getCollection($collectionName);
            $parseCondition = array();
            $this->_queryCondition($condition, $parseCondition);
            $parseNew = array();
            if (isset($new['set']))
            {
                $setList = $new['set'];
                $this->_write_value($setList);
                foreach ($setList as $k => $v)
                {
                    $parseNew['$set'][$k] = $v;
                }
            }
            if (isset($new['inc']))
            {
                $incList = $new['inc'];
                $this->_write_value($incList);
                foreach ($incList as $k => $v)
                {
                    $parseNew['$inc'][$k] = $v;
                }
            }
            $options['multiple'] = true; //满足条件的，全部更新
            return $collection->update($parseCondition, $parseNew, $options);
        }
        return true;
    }

    /**
     * 
     * @param type $condition
     * @param type $rs
     * @return type
     */
    private function _queryCondition($condition = array(), &$rs = array())
    {
        if ($condition)
        {
            //and
            if (isset($condition[ParseQuery::TAG_AND]))
            {
                $andList = $condition[ParseQuery::TAG_AND];
                foreach ($andList as $k => $tagList)
                {
                    foreach ($tagList as $tag => $vList)
                    {
                        $sign = $this->_sign_to_sql($tag);
                        if ($sign)
                        {
                            foreach ($vList as $v)
                            {
                                $v = is_int($v) && $v > 2147483647 ? new MongoInt64($v) : $v;
                                $rs[$k][$sign] = $v;
                            }
                        }
                    }
                }
            }
            //in
            if (isset($condition[ParseQuery::TAG_IN]))
            {
                $inList = $condition[ParseQuery::TAG_IN];
                foreach ($inList as $k => $vList)
                {
                    foreach ($vList as $v)
                    {
                        $v = is_int($v) && $v > 2147483647 ? new MongoInt64($v) : $v;
                        $rs[$k]['$in'][] = $v;
                    }
                }
            }
        }
        return;
    }

    /**
     * 
     * @param type $str
     * @return boolean|string
     */
    private function _sign_to_sql($str)
    {
        $rs = '';
        switch ($str)
        {
            case ParseQuery::TAG_EQ:
                $rs = '$eq';
                break;
            case ParseQuery::TAG_GT:
                $rs = '$gt';
                break;
            case ParseQuery::TAG_LT:
                $rs = '$lt';
                break;
            case ParseQuery::TAG_GTE:
                $rs = '$gte';
                break;
            case ParseQuery::TAG_LTE:
                $rs = '$lte';
                break;
            case ParseQuery::TAG_NE:
                $rs = '$ne';
                break;
        }
        if (empty($rs))
            return false;
        return $rs;
    }

    private function _write_value(&$new)
    {
        foreach ($new as $key => $value)
        {
            if (is_int($value) && $value > 2147483647)
            {
                $new[$key] = new MongoInt64($value);
            }
        }
    }

}
